package com.yungam.imcs.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yungam.common.bean.equip.EquipStableConfig;
import com.yungam.common.bean.equip.EquipStableScore;
import com.yungam.common.bean.equip.Equipment;
import com.yungam.common.bean.process.ProductionProcess;
import com.yungam.common.service.EquipStableConfigService;
import com.yungam.common.service.EquipmentService;
import com.yungam.common.service.ProductionProcessService;
import com.yungam.exception.bean.BusinessException;
import com.yungam.exception.bean.ErrorCodeEnum;
import com.yungam.influxdb.service.StableInfluxService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

/**
 * @author liuyicheng
 * @description: 设备稳定性得分计算工具类
 */
@Slf4j
@Component
public class CalculateScoreUtils {

    /**
     * 需要统计得分的7个设备id
     */
    @Getter
    @Setter
    private List<Integer> equips;

    @Getter
    @Setter
    private Map<Integer, List<EquipStableConfig>> collectorIdMap; // 各设备计算项的映射

    @Resource
    private EquipmentService equipmentService;
    @Resource
    private EquipStableConfigService equipStableConfigService;
    @Resource
    private ProductionProcessService productionProcessService;
    @Resource
    private StableInfluxService stableInfluxService;

    @PostConstruct
    public void getEquipStableConfig() {
        collectorIdMap = new HashMap<>();
        List<EquipStableConfig> list = equipStableConfigService.list();
        for (EquipStableConfig ec : list) {
            List<EquipStableConfig> od = collectorIdMap.getOrDefault(ec.getEquId(), Lists.newArrayList());
            od.add(ec);
            collectorIdMap.put(ec.getEquId(), od);
        }
        equips = new ArrayList<>(collectorIdMap.keySet());
    }

    public List<EquipStableScore> calculate(List<String> productSnList) {
        List<EquipStableScore> ans = Lists.newArrayList();

        // 设备映射
        Map<Integer, Equipment> equipMapper = Maps.newHashMap();
        QueryWrapper<Equipment> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(Equipment.class, col -> !col.getProperty().equals("equImg"))
                .in("equ_id", equips);
        List<Equipment> equipmentList = equipmentService.list(queryWrapper);
        for (Equipment e : equipmentList) equipMapper.put(e.getEquId(), e);
        // 各罐号对应设备的开始 & 结束时间映射 key 由罐号和设备ID拼接而成
        Map<String, Date[]> dateMap = Maps.newHashMap();
        List<ProductionProcess> processList = productionProcessService.list(new QueryWrapper<ProductionProcess>()
                .in("product_sn", productSnList).isNotNull("end_time").eq("state", 1));
        for (ProductionProcess pp : processList) {
            String key = pp.getProductSn() + "-" + pp.getEquNo();
            if (pp.getStartTime() == null || pp.getEndTime() == null) continue;
            dateMap.put(key, new Date[] {pp.getStartTime(), pp.getEndTime()});
        }

        for (String productSn : productSnList) {
            Map<Integer, Integer> core = core(equipMapper, dateMap, productSn);
            for (Integer equId : core.keySet()) {
                EquipStableScore esc = new EquipStableScore();
                esc.setProductSn(productSn).setEquId(equId).setScore(core.get(equId)).setCreateTime(new Date());
                ans.add(esc);
            }
        }

        return ans;
    }

    /**
     * 核心计算
     *      每个罐号各个设备的得分
     */
    private Map<Integer, Integer> core(Map<Integer, Equipment> equipMapper, Map<String, Date[]> dateMap, String productSn) {
        if (equips.size() == 0) {
            throw new BusinessException(ErrorCodeEnum.SYSTEM_ERROR, "系统错误, 未配置设备稳定性计算项");
        }

        Map<Integer, Double> stdMap = Maps.newHashMap(); // 保存各设备的标准差
        for (Integer equId : equips) {
            Equipment curEquip = equipMapper.get(equId);
            String equIp = curEquip.getEquIp();
            // 查找设备对应罐号对应工序的开始结束时间
            String key = productSn + "-" + curEquip.getEquNumber();
            if (!dateMap.containsKey(key)) continue;
            long start = dateMap.get(key)[0].getTime() / 1000, end = dateMap.get(key)[1].getTime() / 1000;
            Long[] range = {start, end};
            // 找到采集项id
            List<EquipStableConfig> configs = collectorIdMap.get(equId);
            if (configs.size() < 2) continue;
            Integer currentId = configs.get(0).getType() == 0 ? configs.get(0).getCollectorItemId() :
                    configs.get(1).getCollectorItemId(),
                    voltageId = configs.get(0).getType() == 1 ? configs.get(0).getCollectorItemId() :
                    configs.get(1).getCollectorItemId();
            // 电流标准差
            double std = stableInfluxService.getCurrentData(equIp, currentId, range) / 2;
            // 电压标准差
            std += stableInfluxService.getVoltageData(equIp, new Integer[] {currentId, voltageId}, range) / 2;
            stdMap.put(equId, std);
        }

        // 将各设备的标准差转换为得分
        return scoreTransform(stdMap);
    }

    /**
     * 将标准差转为得分
     * @return
     */
    private Map<Integer, Integer> scoreTransform(Map<Integer, Double> src) {
        Map<Integer, Integer> res = Maps.newHashMap();
        // 去除标准差中 max 和 min 项, 计算平均值
        List<Double> stds = new ArrayList<>(src.values());
        stds.sort((x, y) -> (int) (x - y));
        int n = stds.size();
        if (n <= 2) return res; //
        Double sum = 0d;
        for (int i = 1; i < n - 1; i++) sum += stds.get(i);
        Double avg = sum / (n - 2);
        // 求得分
        for (Integer key : src.keySet()) {
            int score = 70;
            double diff = avg - src.get(key);
            if (diff == 0) {
                res.put(key, score);
            } else if (diff > 0) {  // 比平均值每少1, 得分+0.3, 最高99分
                 double plus = (0.3 * diff) > 29 ? 29 : 0.3 * diff;
                 score += Math.floor(plus);
                 res.put(key, score);
            } else { // 比平均值每多1, 得分-0.5, 最低20分
                double minus = (0.5 * diff) > 50 ? 50 : 0.5 * diff;
                score -= Math.floor(minus);
                res.put(key, score);
            }
        }
        return res;
    }

}
